home *** CD-ROM | disk | FTP | other *** search
/ Computer Shopper 242 / Issue 242 - April 2008 - DPCS0408DVD.ISO / Software Money Savers / VirtualDub / Source / VirtualDub-1.7.7-src.7z / src / system / source / fileasync.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2006-11-05  |  21.0 KB  |  833 lines

  1. //    VirtualDub - Video processing and capture application
  2. //    System library component
  3. //    Copyright (C) 1998-2004 Avery Lee, All Rights Reserved.
  4. //
  5. //    Beginning with 1.6.0, the VirtualDub system library is licensed
  6. //    differently than the remainder of VirtualDub.  This particular file is
  7. //    thus licensed as follows (the "zlib" license):
  8. //
  9. //    This software is provided 'as-is', without any express or implied
  10. //    warranty.  In no event will the authors be held liable for any
  11. //    damages arising from the use of this software.
  12. //
  13. //    Permission is granted to anyone to use this software for any purpose,
  14. //    including commercial applications, and to alter it and redistribute it
  15. //    freely, subject to the following restrictions:
  16. //
  17. //    1.    The origin of this software must not be misrepresented; you must
  18. //        not claim that you wrote the original software. If you use this
  19. //        software in a product, an acknowledgment in the product
  20. //        documentation would be appreciated but is not required.
  21. //    2.    Altered source versions must be plainly marked as such, and must
  22. //        not be misrepresented as being the original software.
  23. //    3.    This notice may not be removed or altered from any source
  24. //        distribution.
  25.  
  26. #include "stdafx.h"
  27. #include <windows.h>
  28. #include <malloc.h>
  29. #include <vd2/system/error.h>
  30. #include <vd2/system/file.h>
  31. #include <vd2/system/fileasync.h>
  32. #include <vd2/system/thread.h>
  33. #include <vd2/system/vdstl.h>
  34. #include <vd2/system/VDString.h>
  35. #include <vd2/system/VDRingBuffer.h>
  36. #include <vd2/system/w32assist.h>
  37.  
  38. ///////////////////////////////////////////////////////////////////////////
  39. //
  40. //    VDFileAsync - Windows 9x implementation
  41. //
  42. ///////////////////////////////////////////////////////////////////////////
  43.  
  44. class VDFileAsync9x : public IVDFileAsync, protected VDThread {
  45. public:
  46.     VDFileAsync9x(bool useFastMode);
  47.     ~VDFileAsync9x();
  48.  
  49.     void SetPreemptiveExtend(bool b) { mbPreemptiveExtend = b; }
  50.     bool IsPreemptiveExtendActive() { return mbPreemptiveExtend; }
  51.  
  52.     bool IsOpen() { return mhFileSlow != INVALID_HANDLE_VALUE; }
  53.  
  54.     void Open(const wchar_t *pszFilename, uint32 count, uint32 bufferSize);
  55.     void Close();
  56.     void FastWrite(const void *pData, uint32 bytes);
  57.     void FastWriteEnd();
  58.     void Write(sint64 pos, const void *pData, uint32 bytes);
  59.     bool Extend(sint64 pos);
  60.     void Truncate(sint64 pos);
  61.     void SafeTruncateAndClose(sint64 pos);
  62.     sint64 GetSize();
  63.     sint64 GetFastWritePos() { return mClientFastPointer; }
  64.  
  65. protected:
  66.     void WriteZero(sint64 pos, uint32 bytes);
  67.     void Seek(sint64 pos);
  68.     bool SeekNT(sint64 pos);
  69.     void ThrowError();
  70.     void ThreadRun();
  71.  
  72.     HANDLE        mhFileSlow;
  73.     HANDLE        mhFileFast;
  74.     uint32        mBlockSize;
  75.     uint32        mBlockCount;
  76.     uint32        mSectorSize;
  77.     sint64        mClientFastPointer;
  78.  
  79.     const bool        mbUseFastMode;
  80.  
  81.     volatile bool    mbPreemptiveExtend;
  82.  
  83.     enum {
  84.         kStateNormal,
  85.         kStateFlush,
  86.         kStateAbort
  87.     };
  88.     VDAtomicInt    mState;
  89.  
  90.     VDSignal    mReadOccurred;
  91.     VDSignal    mWriteOccurred;
  92.  
  93.     VDRingBuffer<char, VDFileUnbufferAllocator<char> >    mBuffer;
  94.  
  95.     VDStringA    mFilename;
  96.     VDAtomicPtr<MyError>    mpError;
  97. };
  98.  
  99. ///////////////////////////////////////////////////////////////////////////
  100.  
  101. VDFileAsync9x::VDFileAsync9x(bool useFastMode)
  102.     : mhFileSlow(INVALID_HANDLE_VALUE)
  103.     , mhFileFast(INVALID_HANDLE_VALUE)
  104.     , mClientFastPointer(0)
  105.     , mbUseFastMode(useFastMode)
  106.     , mbPreemptiveExtend(false)
  107.     , mpError(NULL)
  108. {
  109. }
  110.  
  111. VDFileAsync9x::~VDFileAsync9x() {
  112.     Close();
  113. }
  114.  
  115. void VDFileAsync9x::Open(const wchar_t *pszFilename, uint32 count, uint32 bufferSize) {
  116.     try {
  117.         mFilename = VDTextWToA(pszFilename);
  118.  
  119.         mhFileSlow = CreateFile(mFilename.c_str(), GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_WRITE_THROUGH, NULL);
  120.         if (mhFileSlow == INVALID_HANDLE_VALUE)
  121.             throw MyWin32Error("Unable to open file \"%s\" for write: %%s", GetLastError(), mFilename.c_str());
  122.  
  123.         if (mbUseFastMode)
  124.             mhFileFast = CreateFile(mFilename.c_str(), GENERIC_WRITE, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_NO_BUFFERING, NULL);
  125.  
  126.         mSectorSize = 4096;        // guess for now... proper way would be GetVolumeMountPoint() followed by GetDiskFreeSpace().
  127.  
  128.         mBlockSize = bufferSize;
  129.         mBlockCount = count;
  130.         mBuffer.Init(count * bufferSize);
  131.  
  132.         mState = kStateNormal;
  133.     } catch(const MyError&) {
  134.         Close();
  135.         throw;
  136.     }
  137.  
  138.     ThreadStart();
  139. }
  140.  
  141. void VDFileAsync9x::Close() {
  142.     mState = kStateAbort;
  143.     mWriteOccurred.signal();
  144.     ThreadWait();
  145.  
  146.     if (mhFileSlow != INVALID_HANDLE_VALUE) {
  147.         CloseHandle(mhFileSlow);
  148.         mhFileSlow = INVALID_HANDLE_VALUE;
  149.     }
  150.     if (mhFileFast != INVALID_HANDLE_VALUE) {
  151.         CloseHandle(mhFileFast);
  152.         mhFileFast = INVALID_HANDLE_VALUE;
  153.     }
  154. }
  155.  
  156. void VDFileAsync9x::FastWrite(const void *pData, uint32 bytes) {
  157.     if (mhFileFast == INVALID_HANDLE_VALUE) {
  158.         if (pData)
  159.             Write(mClientFastPointer, pData, bytes);
  160.         else
  161.             WriteZero(mClientFastPointer, bytes);
  162.     } else {
  163.         if (mpError)
  164.             ThrowError();
  165.  
  166.         uint32 bytesLeft = bytes;
  167.         while(bytesLeft) {
  168.             int actual;
  169.             void *p = mBuffer.LockWrite(bytesLeft, actual);
  170.  
  171.             if (!actual) {
  172.                 mReadOccurred.wait();
  173.                 if (mpError)
  174.                     ThrowError();
  175.                 continue;
  176.             }
  177.  
  178.             if (pData) {
  179.                 memcpy(p, pData, actual);
  180.                 pData = (const char *)pData + actual;
  181.             } else {
  182.                 memset(p, 0, actual);
  183.             }
  184.             mBuffer.UnlockWrite(actual);
  185.             mWriteOccurred.signal();
  186.             bytesLeft -= actual;
  187.         }
  188.     }
  189.  
  190.     mClientFastPointer += bytes;
  191. }
  192.  
  193. void VDFileAsync9x::FastWriteEnd() {
  194.     FastWrite(NULL, mSectorSize - 1);
  195.  
  196.     mState = kStateFlush;
  197.     mWriteOccurred.signal();
  198.     ThreadWait();
  199.  
  200.     if (mpError)
  201.         ThrowError();
  202. }
  203.  
  204. void VDFileAsync9x::Write(sint64 pos, const void *p, uint32 bytes) {
  205.     Seek(pos);
  206.  
  207.     DWORD dwActual;
  208.     if (!WriteFile(mhFileSlow, p, bytes, &dwActual, NULL) || dwActual != bytes)
  209.         throw MyWin32Error("Write error occurred on file \"%s\": %%s\n", GetLastError(), mFilename.c_str());
  210. }
  211.  
  212. void VDFileAsync9x::WriteZero(sint64 pos, uint32 bytes) {
  213.     uint32 bufsize = bytes > 2048 ? 2048 : bytes;
  214.     void *p = _alloca(bufsize);
  215.     memset(p, 0, bufsize);
  216.  
  217.     while(bytes > 0) {
  218.         uint32 tc = bytes > 2048 ? 2048 : bytes;
  219.  
  220.         Write(pos, p, tc);
  221.         pos += tc;
  222.         bytes -= tc;
  223.     }
  224. }
  225.  
  226. bool VDFileAsync9x::Extend(sint64 pos) {
  227.     return SeekNT(pos) && SetEndOfFile(mhFileSlow);
  228. }
  229.  
  230. void VDFileAsync9x::Truncate(sint64 pos) {
  231.     Seek(pos);
  232.     if (!SetEndOfFile(mhFileSlow))
  233.         throw MyWin32Error("I/O error on file \"%s\": %%s", GetLastError(), mFilename.c_str());
  234. }
  235.  
  236. void VDFileAsync9x::SafeTruncateAndClose(sint64 pos) {
  237.     if (mhFileSlow != INVALID_HANDLE_VALUE) {
  238.         FastWrite(NULL, mSectorSize - 1);
  239.  
  240.         mState = kStateFlush;
  241.         mWriteOccurred.signal();
  242.         ThreadWait();
  243.  
  244.         Extend(pos);
  245.         Close();
  246.     }
  247. }
  248.  
  249. sint64 VDFileAsync9x::GetSize() {
  250.     DWORD dwSizeHigh;
  251.     DWORD dwSizeLow = GetFileSize(mhFileSlow, &dwSizeHigh);
  252.  
  253.     if (dwSizeLow == (DWORD)-1 && GetLastError() != NO_ERROR)
  254.         throw MyWin32Error("I/O error on file \"%s\": %%s", GetLastError(), mFilename.c_str());
  255.  
  256.     return dwSizeLow + ((sint64)dwSizeHigh << 32);
  257. }
  258.  
  259. void VDFileAsync9x::Seek(sint64 pos) {
  260.     if (!SeekNT(pos))
  261.         throw MyWin32Error("I/O error on file \"%s\": %%s", GetLastError(), mFilename.c_str());
  262. }
  263.  
  264. bool VDFileAsync9x::SeekNT(sint64 pos) {
  265.     LONG posHi = (LONG)(pos >> 32);
  266.     DWORD result = SetFilePointer(mhFileSlow, (LONG)pos, &posHi, FILE_BEGIN);
  267.  
  268.     if (result == INVALID_SET_FILE_POINTER) {
  269.         DWORD dwError = GetLastError();
  270.  
  271.         if (dwError != NO_ERROR)
  272.             return false;
  273.     }
  274.  
  275.     return true;
  276. }
  277.  
  278. void VDFileAsync9x::ThrowError() {
  279.     MyError *e = mpError.xchg(NULL);
  280.  
  281.     if (e) {
  282.         if (mhFileFast != INVALID_HANDLE_VALUE) {
  283.             CloseHandle(mhFileFast);
  284.             mhFileFast = INVALID_HANDLE_VALUE;
  285.         }
  286.  
  287.         MyError tmp;
  288.         tmp.TransferFrom(*e);
  289.         delete e;
  290.         throw tmp;
  291.     }
  292. }
  293.  
  294. void VDFileAsync9x::ThreadRun() {
  295.     bool    bPreemptiveExtend = mbPreemptiveExtend;
  296.     sint64    currentSize;
  297.     sint64    pos = 0;
  298.     uint32    bufferSize = mBlockCount * mBlockSize;
  299.     HANDLE  hFile = mhFileFast != INVALID_HANDLE_VALUE ? mhFileFast : mhFileSlow;
  300.  
  301.     try {
  302.         if (!VDGetFileSizeW32(hFile, currentSize))
  303.             throw MyWin32Error("I/O error on file \"%s\": %%s", GetLastError(), mFilename.c_str());
  304.  
  305.         for(;;) {
  306.             int state = mState;
  307.  
  308.             if (state == kStateAbort)
  309.                 break;
  310.  
  311.             int actual;
  312.             const void *p = mBuffer.LockRead(mBlockSize, actual);
  313.  
  314.             if ((uint32)actual < mBlockSize) {
  315.                 if (state == kStateNormal) {
  316.                     mWriteOccurred.wait();
  317.                     continue;
  318.                 }
  319.  
  320.                 VDASSERT(state == kStateFlush);
  321.  
  322.                 actual &= ~(mSectorSize-1);
  323.                 if (!actual)
  324.                     break;
  325.             } else {
  326.                 if (bPreemptiveExtend) {
  327.                     sint64 checkpt = pos + mBlockSize + bufferSize;
  328.  
  329.                     if (checkpt > currentSize) {
  330.                         currentSize += bufferSize;
  331.                         if (currentSize < checkpt)
  332.                             currentSize = checkpt;
  333.  
  334.                         if (!VDSetFilePointerW32(hFile, currentSize, FILE_BEGIN)
  335.                             || !SetEndOfFile(hFile))
  336.                             mbPreemptiveExtend = bPreemptiveExtend = false;
  337.  
  338.                         if (!VDSetFilePointerW32(hFile, pos, FILE_BEGIN))
  339.                             throw MyWin32Error("Seek error occurred on file \"%s\": %%s\n", GetLastError(), mFilename.c_str());
  340.                     }
  341.                 }
  342.             }
  343.  
  344.             DWORD dwActual;
  345.             if (!WriteFile(hFile, p, actual, &dwActual, NULL) || dwActual != actual) {
  346.                 DWORD dwError = GetLastError();
  347.                 throw MyWin32Error("Write error occurred on file \"%s\": %%s\n", dwError, mFilename.c_str());
  348.             }
  349.  
  350.             pos += actual;
  351.  
  352.             mBuffer.UnlockRead(actual);
  353.  
  354.             mReadOccurred.signal();
  355.         }
  356.     } catch(MyError& e) {
  357.         MyError *p = new MyError;
  358.  
  359.         p->TransferFrom(e);
  360.         delete mpError.xchg(p);
  361.         mReadOccurred.signal();
  362.     }
  363. }
  364.  
  365. ///////////////////////////////////////////////////////////////////////////
  366. //
  367. //    VDFileAsync - Windows NT implementation
  368. //
  369. ///////////////////////////////////////////////////////////////////////////
  370.  
  371. struct VDFileAsyncNTBuffer : public OVERLAPPED {
  372.     bool    mbActive;
  373.     bool    mbPending;
  374.     uint32    mLength;
  375.  
  376.     VDFileAsyncNTBuffer() : mbActive(false), mbPending(false) { hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); }
  377.     ~VDFileAsyncNTBuffer() { if (hEvent) CloseHandle(hEvent); }
  378. };
  379.  
  380. class VDFileAsyncNT : public IVDFileAsync, private VDThread {
  381. public:
  382.     VDFileAsyncNT();
  383.     ~VDFileAsyncNT();
  384.  
  385.     void SetPreemptiveExtend(bool b) { mbPreemptiveExtend = b; }
  386.     bool IsPreemptiveExtendActive() { return mbPreemptiveExtend; }
  387.  
  388.     bool IsOpen() { return mhFileSlow != INVALID_HANDLE_VALUE; }
  389.  
  390.     void Open(const wchar_t *pszFilename, uint32 count, uint32 bufferSize);
  391.     void Close();
  392.     void FastWrite(const void *pData, uint32 bytes);
  393.     void FastWriteEnd();
  394.     void Write(sint64 pos, const void *pData, uint32 bytes);
  395.     bool Extend(sint64 pos);
  396.     void Truncate(sint64 pos);
  397.     void SafeTruncateAndClose(sint64 pos);
  398.     sint64 GetSize();
  399.     sint64 GetFastWritePos() { return mClientFastPointer; }
  400.  
  401. protected:
  402.     void WriteZero(sint64 pos, uint32 bytes);
  403.     void Seek(sint64 pos);
  404.     bool SeekNT(sint64 pos);
  405.     void ThrowError();
  406.     void ThreadRun();
  407.  
  408.     HANDLE        mhFileSlow;
  409.     HANDLE        mhFileFast;
  410.     uint32        mBlockSize;
  411.     uint32        mBlockCount;
  412.     uint32        mBufferSize;
  413.     uint32        mSectorSize;
  414.  
  415.     enum {
  416.         kStateNormal,
  417.         kStateFlush,
  418.         kStateAbort
  419.     };
  420.     VDAtomicInt    mState;
  421.  
  422.     VDSignal    mReadOccurred;
  423.     VDSignal    mWriteOccurred;
  424.  
  425.     uint32        mWriteOffset;
  426.     VDAtomicInt    mBufferLevel;
  427.     sint64        mClientFastPointer;
  428.     sint64        mFastPointer;
  429.  
  430.     volatile bool    mbPreemptiveExtend;
  431.  
  432.     vdautoarrayptr<VDFileAsyncNTBuffer>    mpBlocks;
  433.  
  434.     vdblock<char, VDFileUnbufferAllocator<char> >    mBuffer;
  435.  
  436.     VDAtomicPtr<MyError>    mpError;
  437.     VDStringA    mFilename;
  438. };
  439.  
  440. VDFileAsyncNT::VDFileAsyncNT()
  441.     : mhFileSlow(INVALID_HANDLE_VALUE)
  442.     , mhFileFast(INVALID_HANDLE_VALUE)
  443.     , mFastPointer(0)
  444.     , mClientFastPointer(0)
  445.     , mbPreemptiveExtend(false)
  446.     , mpError(NULL)
  447. {
  448. }
  449.  
  450. VDFileAsyncNT::~VDFileAsyncNT() {
  451.     Close();
  452. }
  453.  
  454. void VDFileAsyncNT::Open(const wchar_t *pszFilename, uint32 count, uint32 bufferSize) {
  455.     try {
  456.         mFilename = VDTextWToA(pszFilename);
  457.  
  458.         mhFileSlow = CreateFileW(pszFilename, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
  459.         if (mhFileSlow == INVALID_HANDLE_VALUE)
  460.             throw MyWin32Error("Unable to open file \"%s\" for write: %%s", GetLastError(), mFilename.c_str());
  461.  
  462.         mhFileFast = CreateFileW(pszFilename, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_NO_BUFFERING | FILE_FLAG_OVERLAPPED, NULL);
  463.         if (mhFileFast == INVALID_HANDLE_VALUE)
  464.             mhFileFast = CreateFileW(pszFilename, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_WRITE_THROUGH | FILE_FLAG_OVERLAPPED, NULL);
  465.  
  466.         mSectorSize = 4096;        // guess for now... proper way would be GetVolumeMountPoint() followed by GetDiskFreeSpace().
  467.  
  468.         mBlockSize = bufferSize;
  469.         mBlockCount = count;
  470.         mBufferSize = mBlockSize * mBlockCount;
  471.  
  472.         mWriteOffset = 0;
  473.         mBufferLevel = 0;
  474.  
  475.         mState = kStateNormal;
  476.  
  477.         if (mhFileFast != INVALID_HANDLE_VALUE) {
  478.             mpBlocks = new VDFileAsyncNTBuffer[count];
  479.             mBuffer.resize(count * bufferSize);
  480.             ThreadStart();
  481.         }
  482.     } catch(const MyError&) {
  483.         Close();
  484.         throw;
  485.     }
  486. }
  487.  
  488. void VDFileAsyncNT::Close() {
  489.     mState = kStateAbort;
  490.     mWriteOccurred.signal();
  491.     ThreadWait();
  492.  
  493.     if (mpError) {
  494.         delete mpError;
  495.         mpError = NULL;
  496.     }
  497.  
  498.     if (mhFileSlow != INVALID_HANDLE_VALUE) {
  499.         CloseHandle(mhFileSlow);
  500.         mhFileSlow = INVALID_HANDLE_VALUE;
  501.     }
  502.     if (mhFileFast != INVALID_HANDLE_VALUE) {
  503.         CloseHandle(mhFileFast);
  504.         mhFileFast = INVALID_HANDLE_VALUE;
  505.     }
  506.  
  507.     mpBlocks = NULL;
  508. }
  509.  
  510. void VDFileAsyncNT::FastWrite(const void *pData, uint32 bytes) {
  511.     if (mhFileFast == INVALID_HANDLE_VALUE) {
  512.         if (pData)
  513.             Write(mClientFastPointer, pData, bytes);
  514.         else
  515.             WriteZero(mClientFastPointer, bytes);
  516.     } else {
  517.         if (mpError)
  518.             ThrowError();
  519.  
  520.         uint32 bytesLeft = bytes;
  521.         while(bytesLeft) {
  522.             uint32 actual = mBufferSize - mBufferLevel;
  523.  
  524.             if (actual > bytesLeft)
  525.                 actual = bytesLeft;
  526.  
  527.             if (mWriteOffset + actual > mBufferSize)
  528.                 actual = mBufferSize - mWriteOffset;
  529.  
  530.             if (!actual) {
  531.                 mReadOccurred.wait();
  532.                 if (mpError)
  533.                     ThrowError();
  534.                 continue;
  535.             }
  536.  
  537.             if (pData) {
  538.                 memcpy(&mBuffer[mWriteOffset], pData, actual);
  539.                 pData = (const char *)pData + actual;
  540.             } else {
  541.                 memset(&mBuffer[mWriteOffset], 0, actual);
  542.             }
  543.  
  544.             uint32 oldWriteOffset = mWriteOffset;
  545.             mWriteOffset += actual;
  546.             if (mWriteOffset >= mBufferSize)
  547.                 mWriteOffset = 0;
  548.             mBufferLevel += actual;
  549.  
  550.             // only bother signaling if the write offset crossed a block boundary
  551.             if (oldWriteOffset % mBlockSize + actual >= mBlockSize) {
  552.                 mWriteOccurred.signal();
  553.                 if (mpError)
  554.                     ThrowError();
  555.             }
  556.  
  557.             bytesLeft -= actual;
  558.         }
  559.     }
  560.         
  561.     mClientFastPointer += bytes;
  562. }
  563.  
  564. void VDFileAsyncNT::FastWriteEnd() {
  565.     FastWrite(NULL, mSectorSize - 1);
  566.     mState = kStateFlush;
  567.     mWriteOccurred.signal();
  568.     ThreadWait();
  569.     if (mpError)
  570.         ThrowError();
  571. }
  572.  
  573. void VDFileAsyncNT::Write(sint64 pos, const void *p, uint32 bytes) {
  574.     Seek(pos);
  575.  
  576.     DWORD dwActual;
  577.     if (!WriteFile(mhFileSlow, p, bytes, &dwActual, NULL) || dwActual != bytes)
  578.         throw MyWin32Error("Write error occurred on file \"%s\": %%s", GetLastError(), mFilename.c_str());
  579. }
  580.  
  581. void VDFileAsyncNT::WriteZero(sint64 pos, uint32 bytes) {
  582.     uint32 bufsize = bytes > 2048 ? 2048 : bytes;
  583.     void *p = _alloca(bufsize);
  584.     memset(p, 0, bufsize);
  585.  
  586.     while(bytes > 0) {
  587.         uint32 tc = bytes > 2048 ? 2048 : bytes;
  588.  
  589.         Write(pos, p, tc);
  590.         pos += tc;
  591.         bytes -= tc;
  592.     }
  593. }
  594.  
  595. bool VDFileAsyncNT::Extend(sint64 pos) {
  596.     return SeekNT(pos) && SetEndOfFile(mhFileSlow);
  597. }
  598.  
  599. void VDFileAsyncNT::Truncate(sint64 pos) {
  600.     Seek(pos);
  601.     if (!SetEndOfFile(mhFileSlow))
  602.         throw MyWin32Error("I/O error on file \"%s\": %%s", GetLastError(), mFilename.c_str());
  603. }
  604.  
  605. void VDFileAsyncNT::SafeTruncateAndClose(sint64 pos) {
  606.     if (isThreadAttached()) {
  607.         mState = kStateAbort;
  608.         mWriteOccurred.signal();
  609.         ThreadWait();
  610.  
  611.         if (mpError) {
  612.             delete mpError;
  613.             mpError = NULL;
  614.         }
  615.     }
  616.  
  617.     if (mhFileSlow != INVALID_HANDLE_VALUE) {
  618.         Extend(pos);
  619.         Close();
  620.     }
  621. }
  622.  
  623. sint64 VDFileAsyncNT::GetSize() {
  624.     DWORD dwSizeHigh;
  625.     DWORD dwSizeLow = GetFileSize(mhFileSlow, &dwSizeHigh);
  626.  
  627.     if (dwSizeLow == (DWORD)-1 && GetLastError() != NO_ERROR)
  628.         throw MyWin32Error("I/O error on file \"%s\": %%s", GetLastError(), mFilename.c_str());
  629.  
  630.     return dwSizeLow + ((sint64)dwSizeHigh << 32);
  631. }
  632.  
  633. void VDFileAsyncNT::Seek(sint64 pos) {
  634.     if (!SeekNT(pos))
  635.         throw MyWin32Error("I/O error on file \"%s\": %%s", GetLastError(), mFilename.c_str());
  636. }
  637.  
  638. bool VDFileAsyncNT::SeekNT(sint64 pos) {
  639.     LONG posHi = (LONG)(pos >> 32);
  640.     DWORD result = SetFilePointer(mhFileSlow, (LONG)pos, &posHi, FILE_BEGIN);
  641.  
  642.     if (result == INVALID_SET_FILE_POINTER) {
  643.         DWORD dwError = GetLastError();
  644.  
  645.         if (dwError != NO_ERROR)
  646.             return false;
  647.     }
  648.  
  649.     return true;
  650. }
  651.  
  652. void VDFileAsyncNT::ThrowError() {
  653.     MyError *e = mpError.xchg(NULL);
  654.  
  655.     if (e) {
  656.         if (mhFileFast != INVALID_HANDLE_VALUE) {
  657.             CloseHandle(mhFileFast);
  658.             mhFileFast = INVALID_HANDLE_VALUE;
  659.         }
  660.  
  661.         MyError tmp;
  662.         tmp.TransferFrom(*e);
  663.         delete e;
  664.         throw tmp;
  665.     }
  666. }
  667.  
  668. void VDFileAsyncNT::ThreadRun() {
  669.     int requestHead = 0;
  670.     int requestTail = 0;
  671.     int requestCount = mBlockCount;
  672.     uint32 pendingLevel = 0;
  673.     uint32 readOffset = 0;
  674.     bool    bPreemptiveExtend = mbPreemptiveExtend;
  675.     sint64    currentSize;
  676.  
  677.     try {
  678.         if (!VDGetFileSizeW32(mhFileFast, currentSize))
  679.             throw MyWin32Error("I/O error on file \"%s\": %%s", GetLastError(), mFilename.c_str());
  680.  
  681.         for(;;) {
  682.             int state = mState;
  683.  
  684.             if (state == kStateAbort) {
  685.                 typedef BOOL (WINAPI *tpCancelIo)(HANDLE);
  686.                 static const tpCancelIo pCancelIo = (tpCancelIo)GetProcAddress(GetModuleHandle("kernel32"), "CancelIo");
  687.                 pCancelIo(mhFileFast);
  688.                 break;
  689.             }
  690.  
  691.             uint32 actual = mBufferLevel - pendingLevel;
  692.             VDASSERT((int)actual >= 0);
  693.             if (readOffset + actual > mBufferSize)
  694.                 actual = mBufferSize - readOffset;
  695.  
  696.             if (actual < mBlockSize) {
  697.                 if (state == kStateNormal || actual < mSectorSize) {
  698.                     // check for blocks that have completed
  699.                     bool blocksCompleted = false;
  700.                     for(;;) {
  701.                         VDFileAsyncNTBuffer& buf = mpBlocks[requestTail];
  702.  
  703.                         if (!buf.mbActive) {
  704.                             if (state == kStateFlush)
  705.                                 goto all_done;
  706.  
  707.                             if (!blocksCompleted) {
  708.                                 // wait for further writes
  709.                                 mWriteOccurred.wait();
  710.                             }
  711.                             break;
  712.                         }
  713.  
  714.                         if (buf.mbPending) {
  715.                             HANDLE h[2] = {buf.hEvent, mWriteOccurred.getHandle()};
  716.                             DWORD waitResult = WaitForMultipleObjects(2, h, FALSE, INFINITE);
  717.  
  718.                             if (waitResult == WAIT_OBJECT_0+1)    // write pending
  719.                                 break;
  720.  
  721.                             DWORD dwActual;
  722.                             if (!GetOverlappedResult(mhFileFast, &buf, &dwActual, TRUE))
  723.                                 throw MyWin32Error("Write error occurred on file \"%s\": %%s", GetLastError(), mFilename.c_str());
  724.                         }
  725.  
  726.                         buf.mbActive = false;
  727.  
  728.                         blocksCompleted = true;
  729.  
  730.                         if (++requestTail >= requestCount)
  731.                             requestTail = 0;
  732.  
  733.                         mBufferLevel -= buf.mLength;
  734.                         pendingLevel -= buf.mLength;
  735.                         VDASSERT((int)mBufferLevel >= 0);
  736.                         VDASSERT((int)pendingLevel >= 0);
  737.  
  738.                         mReadOccurred.signal();
  739.  
  740.                     }
  741.  
  742.                     if (state == kStateNormal)
  743.                         continue;
  744.                 }
  745.  
  746.                 VDASSERT(state == kStateFlush);
  747.  
  748.                 actual &= ~(mSectorSize-1);
  749.  
  750.                 VDASSERT(actual > 0);
  751.             } else {
  752.                 actual = mBlockSize;
  753.  
  754.                 if (bPreemptiveExtend) {
  755.                     sint64 checkpt = mFastPointer + mBlockSize + mBufferSize;
  756.  
  757.                     if (checkpt > currentSize) {
  758.                         currentSize += mBufferSize;
  759.                         if (currentSize < checkpt)
  760.                             currentSize = checkpt;
  761.  
  762.                         if (!VDSetFilePointerW32(mhFileFast, currentSize, FILE_BEGIN)
  763.                             || !SetEndOfFile(mhFileFast))
  764.                             mbPreemptiveExtend = bPreemptiveExtend = false;
  765.                     }
  766.                 }
  767.             }
  768.  
  769.             // Issue a write to OS
  770.             VDFileAsyncNTBuffer& buf = mpBlocks[requestHead];
  771.  
  772.             VDASSERT(!buf.mbActive);
  773.  
  774.             DWORD dwActual;
  775.  
  776.             buf.Offset = (DWORD)mFastPointer;
  777.             buf.OffsetHigh = (DWORD)((uint64)mFastPointer >> 32);
  778.             buf.Internal = 0;
  779.             buf.InternalHigh = 0;
  780.             buf.mLength = actual;
  781.             buf.mbPending = false;
  782.  
  783.             if (!WriteFile(mhFileFast, &mBuffer[readOffset], actual, &dwActual, &buf)) {
  784.                 if (GetLastError() != ERROR_IO_PENDING)
  785.                     throw MyWin32Error("Write error occurred on file \"%s\": %%s", GetLastError(), mFilename.c_str());
  786.  
  787.                 buf.mbPending = true;
  788.             }
  789.  
  790.             buf.mbActive = true;
  791.  
  792.             pendingLevel += actual;
  793.             VDASSERT(pendingLevel <= (uint32)mBufferLevel);
  794.  
  795.             readOffset += actual;
  796.             VDASSERT(readOffset <= mBufferSize);
  797.             if (readOffset >= mBufferSize)
  798.                 readOffset = 0;
  799.  
  800.             mFastPointer += actual;
  801.  
  802.             if (++requestHead >= requestCount)
  803.                 requestHead = 0;
  804.         }
  805. all_done:
  806.         ;
  807.  
  808.     } catch(MyError& e) {
  809.         MyError *p = new MyError;
  810.  
  811.         p->TransferFrom(e);
  812.         delete mpError.xchg(p);
  813.         mReadOccurred.signal();
  814.     }
  815. }
  816.  
  817. ///////////////////////////////////////////////////////////////////////////
  818.  
  819. IVDFileAsync *VDCreateFileAsync(IVDFileAsync::Mode mode) {
  820.     switch(mode) {
  821.  
  822.         case IVDFileAsync::kModeAsynchronous:
  823.             if (VDIsWindowsNT())
  824.                 return new VDFileAsyncNT;
  825.             // Can't do async I/O. Fall-through to 9x method.
  826.         case IVDFileAsync::kModeThreaded:
  827.             return new VDFileAsync9x(true);
  828.  
  829.         default:
  830.             return new VDFileAsync9x(false);
  831.     }
  832. }
  833.